﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;

namespace Kosmos
{
    public class Ball : AbstractBall
    {
        public const float cst_speed = 0.1f;
        static private Ball _player_ball;
        protected Type _type_ball;

        private const String _path_ball = "Graphics\\Ball";
        private const int _sz_contour = 12; // represente la taille du glow autour de la balle

        public const float _sz_min_ball = 0.1f; //La taille minimal des balle pour les supprimer
        public const float _percent_when_change = 0.1f;//pour la gestion des degradé des balle bleu vers rouge
        public const float _percent_eject_when_move = 0.03f; //pourcentage de l'aire qui est rejeter lors du deplacement du joueur
        public const float _percent_mass = 0.03f;//pourcentage de la mass par rapport a l'air

        static private Texture2D _txt2d_ball;
        static private SpriteBatch _sprite; //surface de dessins


        public Ball(Game g, Vector2 position, float diameter)
            : base(g)
        {
            Body body =
                BodyFactory.Instance.CreateCircleBody
                (((MyGame)g).Simulator, diameter / 2f,
                (float)((diameter / 2f) * (diameter / 2f) * Math.PI) * _percent_mass);
            body.Position = position;
            body.IsStatic = false;
            body.LinearDragCoefficient = 0f;

            Geom = GeomFactory.Instance.CreateCircleGeom
                (((MyGame)g).Simulator, body, diameter / 2f, 100, 1);
            Geom.OnCollision += OnCollision;
            Geom.Tag = this;

            Geom.CollisionCategories = getType(Type.BALL);
            _type_ball = Type.BALL;
        }

        public Ball(Game g)
            : base(g)
        {
        }


        public Type type
        {
            get { return _type_ball; }
        }

        public float top
        {
            get { return Geom.Body.Position.Y - radius; }
            set { Geom.Body.Position = new Vector2(Geom.Body.Position.X, value + radius); }
        }

        public float bottom
        {
            get { return Geom.Body.Position.Y + radius; }
            set { Geom.Body.Position = new Vector2(Geom.Body.Position.X, value - radius); }
        }

        public float left
        {
            get { return Geom.Body.Position.X - radius; }
            set { Geom.Body.Position = new Vector2(value + radius, Geom.Body.Position.Y); }
        }

        public float right
        {
            get { return Geom.Body.Position.X + radius; }
            set { Geom.Body.Position = new Vector2(value - radius, Geom.Body.Position.Y); }
        }

        public float centerX
        {
            get { return Geom.Body.Position.X; }
            set { Geom.Body.Position = new Vector2(value, Geom.Body.Position.Y); }
        }

        public float centerY
        {
            get { return Geom.Body.Position.Y; }
            set { Geom.Body.Position = new Vector2(Geom.Body.Position.X, value); }
        }

        public Vector2 center
        {
            get { return Geom.Body.Position; }
            set { centerX = value.X; centerY = value.Y; }
        }

        public float diameter
        {
            get { return radius * 2f; }
            set
            {
                Geom.SetVertices(Vertices.CreateCircle(value / 2f, 100));
                if (diameter > _sz_min_ball)
                    Geom.Body.Mass = (float)((value / 2f) * (value / 2f) * Math.PI) * _percent_mass;
                else
                    Geom.Body.Mass = 0.01f;
                Geom.Body.Position = new Vector2(Geom.Body.Position.X, Geom.Body.Position.Y);
            }
        }

        public float radius
        {
            get { return Geom.LocalVertices[0].X; }
            set { diameter = 2f * value; }
        }

        public float aire
        {
            get { return (float)(radius * radius * Math.PI); }
            set { diameter = (float)Math.Sqrt(value / Math.PI) * 2f; }
        }

        static public Ball player_ball
        {
            set
            {
                _player_ball = value;
            }
        }

        //Obtenir la couleur de la balle en fonction de la taille de la balle du joueur
        protected virtual Color getColor()
        {
            Color c = new Color(0, 150, 255);//Bleu
            if (this == _player_ball)
            {
                c = new Color(0, 220, 255);//Cyan
            }
            else if (diameter < _player_ball.diameter &&
                diameter > _player_ball.diameter - _player_ball.diameter * _percent_when_change)
            {
                float x = 1f - (diameter / _player_ball.diameter);

                float b = 255;
                float r = 255 * 2f - ((x * 255f) / (_percent_when_change / 2));
                float v = ((x * 50f) / (_percent_when_change / 2)) + 50f;

                if (x <= (_percent_when_change / 2f))
                {
                    b = (x * 255f) / (_percent_when_change / 2);
                    r = 255;
                    v = 50;
                }

                c = new Color((byte)r, (byte)v, (byte)b);//Dégradé entre le rouge et bleu
            }
            else if (diameter >= _player_ball.diameter)
            {
                c = new Color(255, 50, 0);//Rouge
            }

            return c;
        }

        public override void Initialize()
        {
            _sprite = ((MyGame)Game).Sprite;

            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {


        }

        public override void Draw(GameTime gameTime)
        {
            //Obtenir la taille de l'image à afficher par rapport au zoom
            Rectangle rect = ((MyGame)Game).Camera.getRectangle(
                Geom.Body.Position.X,
                Geom.Body.Position.Y,
                diameter,
                diameter
            );

            //Augmente la taille de l'image pour qu'elle remplisse le volume réel.
            //voir le glow autour de la balle
            rect.Width += (int)(((double)_sz_contour / (double)_txt2d_ball.Width) * diameter * 2.0 * ((MyGame)Game).Camera.Zoom);
            rect.Height += (int)(((double)_sz_contour / (double)_txt2d_ball.Height) * diameter * 2.0 * ((MyGame)Game).Camera.Zoom);

            //Si l'image a afficher est invisible on sort
            if (!((MyGame)Game).Camera.isVisible(rect)) return;
            _sprite.Draw
            (
                _txt2d_ball,
                rect,
                null,
                getColor(),
                0, //Geom.Body.Rotation,
                new Vector2(_txt2d_ball.Width / 2f, _txt2d_ball.Height / 2f),
                SpriteEffects.None,
                0
            );
        }

        protected override void LoadContent()
        {
            _txt2d_ball = TexturesPool.GetTexture(_path_ball);
        }

        protected override void UnloadContent()
        {
        }

        //Obtenir l'aire d'intersection entre deux balle
        public static float getAireIntersect(Ball b1, Ball b2)
        {


            float dist = (float)Math.Sqrt(
                  Math.Pow(b1.centerX - b2.centerX, 2)
                + Math.Pow(b1.centerY - b2.centerY, 2)
            );

            float h = (float)((double)(1f / (2 * dist)) * Math.Sqrt
               (Math.Abs(
               (Math.Pow(b2.radius + b1.radius, 2) - Math.Pow(dist, 2))
               * (Math.Pow(dist, 2) - (Math.Pow(b2.radius - b1.radius, 2))))));

            float aire = (float)(Math.Pow(b1.radius, 2) * Math.Asin(Math.Min(1f, h / b1.radius))
                + Math.Pow(b2.radius, 2) * Math.Asin(Math.Min(1f, h / b2.radius))
                - (double)(h * dist));

            if (b1.radius > b2.radius)
            {
                if (b1.radius > dist)
                    aire = b2.aire;
            }
            else
            {
                if (b2.radius > dist)
                    aire = b1.aire;
            }

            return aire;
        }

        
        protected static void absorbBall(Ball ball_big, Ball ball_small, float aire)
        {
            ball_big.aire += aire;
            ball_small.aire -= aire;
        }


        //Collision entre deux balle standard
        protected static void CollisionBallWithBall(Ball b1, Ball b2)
        {
            float aire = getAireIntersect(b1, b2);

            if (aire < 0) return;

            aire *= ((b1.radius < b2.radius) ? -1f : 1f);
            absorbBall(b1, b2, aire);
        }



        //collision entre une balle standard et un soleil
        protected static void CollisionBallWithSun(Ball b1, Ball b2)
        {
            float aire = getAireIntersect(b1, b2);

            if (aire < 0) return;

            aire *= (b1.type != Type.SUN) ? -1f : 1f;
            absorbBall(b1, b2, aire);
        }


        //collision entre une balle standard et une balle verte
        protected static void CollisionBallWithGreenBall(Ball b1, Ball b2)
        {
            float aire = getAireIntersect(b1, b2);

            if (aire < 0) return;

            b1.aire -= aire;
            b2.aire -= aire;
        }

        public override string ToString()
        {
            return "Balle : pos = {" + centerX + "," + centerY + "}, aire = " + aire;
        }

        //Listener des collisions
        protected virtual bool OnCollision(Geom geom1, Geom geom2, ContactList contactList)
        {
            switch ((Type)geom1.CollisionCategories)
            {
                case Type.BALL:
                    switch ((Type)geom2.CollisionCategories)
                    {
                        case Type.BALL:
                            CollisionBallWithBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;

                        case Type.BORDER:
                            return true;

                        case Type.SUN:
                            CollisionBallWithSun((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;

                        case Type.GREEN:
                            CollisionBallWithGreenBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;
                    }
                    break;

                case Type.SUN:
                    switch ((Type)geom2.CollisionCategories)
                    {
                        case Type.BALL:
                            CollisionBallWithSun((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;

                        case Type.BORDER:
                            return true;

                        case Type.SUN:
                            CollisionBallWithBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;

                        case Type.GREEN:
                            CollisionBallWithGreenBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;
                    }
                    break;

                case Type.GREEN:
                    switch ((Type)geom2.CollisionCategories)
                    {
                        case Type.BALL:
                            CollisionBallWithGreenBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;

                        case Type.BORDER:
                            return true;

                        case Type.SUN:
                            CollisionBallWithGreenBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;

                        case Type.GREEN:
                            CollisionBallWithBall((Ball)geom1.Tag, (Ball)geom2.Tag);
                            break;
                    }
                    break;

                default:
                    return true;
            }


            return false;
        }

    }
}